// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/aura/window_targeter.h"

#include "ui/aura/client/capture_client.h"
#include "ui/aura/client/event_client.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/events/event_target.h"
#include "ui/events/event_target_iterator.h"

namespace aura {

WindowTargeter::WindowTargeter() { }
WindowTargeter::~WindowTargeter() { }

Window* WindowTargeter::FindTargetForLocatedEvent(Window* window,
    ui::LocatedEvent* event)
{
    if (!window->parent()) {
        Window* target = FindTargetInRootWindow(window, *event);
        if (target) {
            window->ConvertEventToTarget(target, event);
            return target;
        }
    }
    return FindTargetForLocatedEventRecursively(window, event);
}

bool WindowTargeter::SubtreeCanAcceptEvent(
    Window* window,
    const ui::LocatedEvent& event) const
{
    if (!window->IsVisible())
        return false;
    if (window->ignore_events())
        return false;
    client::EventClient* client = client::GetEventClient(window->GetRootWindow());
    if (client && !client->CanProcessEventsWithinSubtree(window))
        return false;

    Window* parent = window->parent();
    if (parent && parent->delegate_ && !parent->delegate_->ShouldDescendIntoChildForEventHandling(window, event.location())) {
        return false;
    }
    return true;
}

bool WindowTargeter::EventLocationInsideBounds(
    Window* window,
    const ui::LocatedEvent& event) const
{
    gfx::Point point = event.location();
    if (window->parent())
        Window::ConvertPointToTarget(window->parent(), window, &point);
    return gfx::Rect(window->bounds().size()).Contains(point);
}

ui::EventTarget* WindowTargeter::FindTargetForEvent(ui::EventTarget* root,
    ui::Event* event)
{
    Window* window = static_cast<Window*>(root);
    Window* target = event->IsKeyEvent()
        ? FindTargetForKeyEvent(window, *static_cast<ui::KeyEvent*>(event))
        : FindTargetForNonKeyEvent(window, event);
    if (target && !window->parent() && !window->Contains(target)) {
        // |window| is the root window, but |target| is not a descendent of
        // |window|. So do not allow dispatching from here. Instead, dispatch the
        // event through the WindowEventDispatcher that owns |target|.
        Window* new_root = target->GetRootWindow();
        DCHECK(new_root);
        if (event->IsLocatedEvent()) {
            // The event has been transformed to be in |target|'s coordinate system.
            // But dispatching the event through the EventProcessor requires the event
            // to be in the host's coordinate system. So, convert the event to be in
            // the root's coordinate space, and then to the host's coordinate space by
            // applying the host's transform.
            ui::LocatedEvent* located_event = static_cast<ui::LocatedEvent*>(event);
            located_event->ConvertLocationToTarget(target, new_root);
            located_event->UpdateForRootTransform(
                new_root->GetHost()->GetRootTransform());
        }
        ignore_result(
            new_root->GetHost()->event_processor()->OnEventFromSource(event));

        target = nullptr;
    }
    return target;
}

ui::EventTarget* WindowTargeter::FindNextBestTarget(
    ui::EventTarget* previous_target,
    ui::Event* event)
{
    return nullptr;
}

bool WindowTargeter::SubtreeShouldBeExploredForEvent(
    Window* window,
    const ui::LocatedEvent& event)
{
    return SubtreeCanAcceptEvent(window, event) && EventLocationInsideBounds(window, event);
}

Window* WindowTargeter::FindTargetForKeyEvent(Window* window,
    const ui::KeyEvent& key)
{
    Window* root_window = window->GetRootWindow();
    client::FocusClient* focus_client = client::GetFocusClient(root_window);
    Window* focused_window = focus_client->GetFocusedWindow();
    if (!focused_window)
        return window;

    client::EventClient* event_client = client::GetEventClient(root_window);
    if (event_client && !event_client->CanProcessEventsWithinSubtree(focused_window)) {
        focus_client->FocusWindow(nullptr);
        return nullptr;
    }
    return focused_window ? focused_window : window;
}

Window* WindowTargeter::FindTargetForNonKeyEvent(Window* root_window,
    ui::Event* event)
{
    if (!event->IsLocatedEvent())
        return root_window;
    return FindTargetForLocatedEvent(root_window,
        static_cast<ui::LocatedEvent*>(event));
}

Window* WindowTargeter::FindTargetInRootWindow(Window* root_window,
    const ui::LocatedEvent& event)
{
    DCHECK_EQ(root_window, root_window->GetRootWindow());

    // Mouse events should be dispatched to the window that processed the
    // mouse-press events (if any).
    if (event.IsScrollEvent() || event.IsMouseEvent()) {
        WindowEventDispatcher* dispatcher = root_window->GetHost()->dispatcher();
        if (dispatcher->mouse_pressed_handler())
            return dispatcher->mouse_pressed_handler();
    }

    // All events should be directed towards the capture window (if any).
    Window* capture_window = client::GetCaptureWindow(root_window);
    if (capture_window)
        return capture_window;

    if (event.IsTouchEvent()) {
        // Query the gesture-recognizer to find targets for touch events.
        const ui::TouchEvent& touch = static_cast<const ui::TouchEvent&>(event);
        ui::GestureConsumer* consumer = ui::GestureRecognizer::Get()->GetTouchLockedTarget(touch);
        if (consumer)
            return static_cast<Window*>(consumer);
        consumer = ui::GestureRecognizer::Get()->GetTargetForLocation(
            event.location_f(), touch.source_device_id());
        if (consumer)
            return static_cast<Window*>(consumer);

        // If the initial touch is outside the root window, target the root.
        if (!root_window->bounds().Contains(event.location()))
            return root_window;
    }

    return nullptr;
}

Window* WindowTargeter::FindTargetForLocatedEventRecursively(
    Window* root_window,
    ui::LocatedEvent* event)
{
    scoped_ptr<ui::EventTargetIterator> iter = root_window->GetChildIterator();
    if (iter) {
        ui::EventTarget* target = root_window;
        for (ui::EventTarget* child = iter->GetNextTarget(); child;
             child = iter->GetNextTarget()) {
            WindowTargeter* targeter = static_cast<WindowTargeter*>(child->GetEventTargeter());
            if (!targeter)
                targeter = this;
            if (!targeter->SubtreeShouldBeExploredForEvent(
                    static_cast<Window*>(child), *event)) {
                continue;
            }
            target->ConvertEventToTarget(child, event);
            target = child;
            Window* child_target_window = static_cast<Window*>(targeter->FindTargetForEvent(child, event));
            if (child_target_window)
                return child_target_window;
        }
        target->ConvertEventToTarget(root_window, event);
    }
    return root_window->CanAcceptEvent(*event) ? root_window : nullptr;
}

} // namespace aura
